#include "json.h"

#include <sstream>

//===================================================================
//
// JsonValue
//
//===================================================================

void JsonValuesMap::clear()
{
    m_mpIntValue.clear();
    m_mpStrValue.clear();
}

void JsonValuesMap::addIntValue(const char *pchKey, int iValue)
{
    std::string sKey(pchKey);
    m_mpIntValue[sKey] = iValue;
}

void JsonValuesMap::addStrValue(const char *pchKey, const char *pchValue)
{
    std::string sKey(pchKey);
    std::string sValue(pchValue);
    m_mpStrValue[sKey] = sValue;
}

bool JsonValuesMap::isStrValue(const char *pchKey)
{
    std::string sKey(pchKey);
    return isStrValue(sKey);
}

bool JsonValuesMap::isStrValue(std::string sKey)
{
    std::map<std::string, std::string>::iterator it;
    it = m_mpStrValue.find(sKey);
    return it != m_mpStrValue.end();
}

bool JsonValuesMap::isIntValue(const char *pchKey)
{
    std::string sKey(pchKey);
    return isIntValue(sKey);
}

bool JsonValuesMap::isIntValue(std::string sKey)
{
    std::map<std::string, int>::iterator it;
    it = m_mpIntValue.find(sKey);
    return it != m_mpIntValue.end();
}

std::string JsonValuesMap::getStrValue(const char *pchKey)
{
    std::string sKey(pchKey);
    return m_mpStrValue[sKey];
}

std::string JsonValuesMap::getStrValue(std::string sKey)
{
    return m_mpStrValue[sKey];
}

int JsonValuesMap::getIntValue(const char *pchKey)
{
    std::string sKey(pchKey);
    return m_mpIntValue[sKey];
}

int JsonValuesMap::getIntValue(std::string sKey)
{
    return m_mpIntValue[sKey];
}

//===================================================================
//
// JsonParser
//
//===================================================================

JsonParser::JsonParser()
{
    reset();
}

JsonParser::~JsonParser()
{

}

/** Регистрация объектов слушателей, которые будут получать асинхронную
  * информацию об извлеченных из входного потока JSON-объектов и об
  * ошибках извлечения. Слушателями являются подклассы абстрактного
  * класса JsonListener.
  */
void JsonParser::addListener(JsonListener *obj)
{
    m_vListener.push_back(obj);
}

/** Подготовка парсера к извлечению нового JSON-объекта.
  *
  */
void JsonParser::reset()
{
    m_ucNL = 0; // флаг переноса
    m_iRow = 0; // счетчик строк
    m_iCol = 0; // счетчик колонок (позиция символа в строке)
    m_zLastError.clear();
    m_jsonValue.clear();
    m_state = PS_START;
}

/** Перебирает зарегистрированных слушателей и передает каждому
  * параметры, извлеченные из полученного JSON-объекта.
  */
void JsonParser::readyRead()
{
    std::vector<JsonListener*>::iterator it;
    for(it = m_vListener.begin(); it<m_vListener.end(); it++) {
        (*it)->readyRead(&m_jsonValue);
    }
}

/** Потоковый парсер JSON-объектов. Может получать данные порциями.
  * При завершении очередного JSON-объекта передает информацию об
  * извлеченных параметрах объекта слушателям парсера.
  */
void JsonParser::parse(const char *data, int len)
{
    bool bNext = true;
    for(int i=0; i<len; (bNext)?++i:i ) {
        bNext = true;
        char ch = data[i];

        bool bSpace = false;
        if (ch == 0x0a || ch == 0x0d) {
            bSpace = true;
            if (m_ucNL == 0) m_ucNL = ch;
            /* такое разделение условий позволит переключатся только по первому коду!!! */
            if (m_ucNL == ch) {
                ++m_iRow; // инкрементируем счетчик строк
            }
            m_iCol = 0;
        } else {
            if  (ch == 0x20 || ch == 0x09) bSpace = true;
            m_ucNL = 0;
            ++m_iCol;
        }

        switch (m_state) {
        case PS_START:
            if (bSpace) continue;
            if (ch == '{') {
                m_jsonValue.clear();
                m_strParser.reset();
                m_state = PS_KEY;
            } else {
            }
            break;
        case PS_KEY:
            if (bSpace) continue;
            if (ch == ':') {
                m_zCurrentKey.clear();
                if (m_strParser.isOk()) {
                    m_zCurrentKey.append(m_strParser.getValue());
                    m_state = PS_VALUE;
                } else {
                    m_zLastError.append(m_strParser.getLastError());
                    bNext = false;
                    m_state = PS_ERROR;
                }
            } else {
                m_strParser.append(ch);
            }
            break;
        case PS_VALUE:
            if (bSpace) continue;
            if (ch == '"') {
                bNext = false;
                m_strParser.reset();
                m_state = PS_STR_VALUE;
            } else {
                if (ch >= '0' && ch <= '9') {
                    bNext = false;
                    m_intParser.reset();
                    m_state = PS_INT_VALUE;
                } else {
                    m_zLastError.append("Unrecognize symbol: ");
                    m_zLastError.append(ch);
                    bNext = false;
                    m_state = PS_ERROR;
                }
            }
            break;
        case PS_INT_VALUE:
            if (ch == ',') {
                if (m_intParser.isOk()) {
                    m_jsonValue.addIntValue(m_zCurrentKey.data(), m_intParser.getValue());
                    m_strParser.reset();
                    m_state = PS_KEY;
                } else {
                    m_zLastError.append(m_intParser.getLastError());
                    bNext = false;
                    m_state = PS_ERROR;
                }
            } else {
                if (ch == '}') {
                    if (m_intParser.isOk()) {
                        m_jsonValue.addIntValue(m_zCurrentKey.data(), m_intParser.getValue());
                        readyRead();
                        reset();
                    } else {
                        m_zLastError.append(m_intParser.getLastError());
                        bNext = false;
                        m_state = PS_ERROR;
                    }
                } else {
                    if (bSpace) m_intParser.appendSpace();
                    else m_intParser.append(ch);
                }
            }
            break;
        case PS_STR_VALUE:
            if (ch == ',') {
                if (m_strParser.isValueMode()) {
                    m_strParser.append(ch);
                } else {
                    if (m_strParser.isOk()) {
                        m_jsonValue.addStrValue(m_zCurrentKey.data(), m_strParser.getValue());
                        m_strParser.reset();
                        m_state = PS_KEY;
                    } else {
                        m_zLastError.append(m_strParser.getLastError());
                        bNext = false;
                        m_state = PS_ERROR;
                    }
                }
            } else {
                if (m_strParser.isValueMode()) {
                    m_strParser.append(ch);
                } else {
                    if (ch == '}') {
                        if (m_strParser.isOk()) {
                            m_jsonValue.addStrValue(m_zCurrentKey.data(),
                                                    m_strParser.getValue());
                            readyRead();
                            reset();
                        } else {
                            m_zLastError.append(m_strParser.getLastError());
                            bNext = false;
                            m_state = PS_ERROR;
                        }
                    } else {
                        if (bSpace) m_strParser.appendSpace(ch);
                        else m_strParser.append(ch);
                    }
                }
            }
            break;
        case PS_ERROR:
        {
            std::stringstream ss;
            ss << "[" << m_iRow << ", " << m_iCol << "] ";
            ss << m_zLastError.data();
            std::string sError(ss.str());

            std::vector<JsonListener*>::iterator it;
            for(it = m_vListener.begin(); it<m_vListener.end(); it++) {
                (*it)->parseError(sError);
            }
            reset();
        }
            break;
        } // switch
    }
}

//===================================================================
//
// StringParser
//
//===================================================================

StringParser::StringParser()
{
    reset();
}

void StringParser::reset()
{
    m_state = PS_START;
    bMaskMode = false;
    m_zValue.clear();
    m_zLastError.clear();
    m_zLastError.append("Expected string");
}

/** Разделение методов передачи в КА пробельных и непробельных символов
  * позволяет экономить на повторных проверках принадлежности символов
  * классам пробельных и непробельных символов.
  *
  */
void StringParser::append(char ch)
{
    switch (m_state) {
    case PS_START:
        if (ch == '"') {
            m_state = PS_VALUE;
        }
        break;
    case PS_VALUE:
        if (ch == '\\') {
            if (bMaskMode) {
                m_zValue.append('\\');
                bMaskMode = false;
            } else {
                bMaskMode = true;
            }
        } else {
            if (ch == '"' && ( ! bMaskMode )) {
                m_state = PS_STOP;
            } else {
                m_zValue.append(ch);
            }
            bMaskMode = false;
        }
        break;
    case PS_STOP:
        // в этом режиме могут быть только пробелы
        m_state = PS_ERROR;
    case PS_ERROR:
        break;
    }
}

/** Разделение методов передачи в КА пробельных и непробельных символов
  * позволяет экономить на повторных проверках принадлежности символов
  * классам пробельных и непробельных символов.
  *
  * Так как в состоянии PS_VALUE пробельный символ должен быть записан
  * в извлекаемую строку как есть, то метод требует текущих пробельный
  * символ в качестве аргумента.
  */
void StringParser::appendSpace(char ch)
{
    switch (m_state) {
    case PS_START:
        break;
    case PS_VALUE:
        m_zValue.append(ch);
        break;
    case PS_STOP:
        break;
    case PS_ERROR:
        break;
    }
}

//===================================================================
//
// IntParser
//
//===================================================================

IntParser::IntParser()
{
    reset();
}

void IntParser::reset()
{
    m_state = PS_START;
    m_iValue = 0;
    m_zLastError.append("Expected integer value");
}

/** Разделение методов передачи в КА пробельных и непробельных символов
  * позволяет экономить на повторных проверках принадлежности символов
  * классам пробельных и непробельных символов.
  *
  */
void IntParser::append(char ch)
{
    switch (m_state) {
    case PS_START:
        m_state = PS_VALUE;
        // продолжаем без break !!!
    case PS_VALUE:
        if (ch >= '0' && ch <= '9') {
            m_iValue = m_iValue*10 + (ch - '0');
        } else {
            m_zLastError.clear();
            m_zLastError.append("Unrecognize symbol: ");
            m_zLastError.append(ch);
            m_state = PS_ERROR;
        }
        break;
    case PS_STOP:
        // в этом режиме могут быть только пробелы
        m_state = PS_ERROR;
    case PS_ERROR:
        break;
    }
}

/** Разделение методов передачи в КА пробельных и непробельных символов
  * позволяет экономить на повторных проверках принадлежности символов
  * классам пробельных и непробельных символов.
  *
  */
void IntParser::appendSpace()
{
    switch (m_state) {
    case PS_START:
        break;
    case PS_VALUE:
        m_state = PS_STOP;
        break;
    case PS_STOP:
        break;
    case PS_ERROR:
        break;
    }
}
